import { request, gql } from "graphql-request";
import webconfig from "../webconfig";
import store from "../utils/store";
import * as antdHelper from "../utils/antd-helper";
import { formatterSizeStr, formatBalanceStr, formatPicUrl } from "../utils/formatter";

//common
function getHeaders(isNeedAuth) {
	if (!isNeedAuth) return;
	return {
		Authorization: store.get("token")
	};
}
async function got(document, isNeedAuth) {
	try {
		let data = await request(webconfig.api, document, null, getHeaders(isNeedAuth));
		return { msg: "ok", data };
	} catch (e) {
		let msg = progressError(e);
		antdHelper.alertError(msg);
		return null;
	}
}
function progressError(e) {
	console.log(e);
	let errMsg = e.message;
	if (e.message.includes("Unauthorized")) {
		errMsg = "Please login again";
		setTimeout(function () {
			if (window.navigate) {
				window.navigate("/login");
			}
		}, 300);
	} else {
		let i = e.message.indexOf("{");
		if (i > 0) {
			i = i - 2;
			errMsg = e.message.slice(0, i);
		}
	}
	return errMsg.replace("Error:", "");
}
function makePager(params) {
	let pageindex = params.pageindex || 1;
	let pagesize = params.pagesize || 20;
	let after = pagesize * (pageindex - 1);
	return `pagination:{after: "${after}", first: ${pagesize}}`;
}
//backend
export async function login(identity, password, remember) {
	const document = gql`
		{
			login(form: { identity:"${identity}", password: "${password}"}) {
				token
				id
				identity
			}
		}
	`;
	let ret = await got(document);
	if (!ret) return false;
	if (ret.msg != "ok" || !ret.data.login || !ret.data.login.token) {
		return false;
	}
	store.set("token", ret.data.login.token);
	store.set("accountId", ret.data.login.id);
	store.set("identity", identity);
	if (remember) {
		store.set("account", {
			identity,
			password
		});
	}
	return true;
}
export async function changePassword(password, newPassword) {
	const document = gql`
		mutation {
			changePassword(form: { password: "${password}", newPassword: "${newPassword}", confirmPassword: "${newPassword}" })
		}
	`;
	let ret = await got(document, true);
	return ret;
}
export async function decloudStatistics() {
	const document = gql`
		{
			decloudStatistics {
				totalUsers {
					dateStr
					value
				}
				newUsersLast24Hr {
					dateStr
					value
				}
				newUsersLastWeek {
					dateStr
					value
				}
				soldStorageSpace
				soldStorageSpaceHistory
				invitedUsers
				totalIncome {
					dateStr
					value
				}
				todayIncome {
					dateStr
					value
				}
				weeklyIncome {
					dateStr
					value
				}
				inviteUserSubsidies
				inviteBonus
			}
		}
	`;
	let ret = await got(document, true);
	if (!ret) return ret;
	return ret.data.decloudStatistics;
}
// only for admin
export async function userDetail(email, address) {
	let filter = {};
	if (email) {
		filter = `{email:"${email}"}`;
	} else {
		filter = `{address:"${address}"}`;
	}
	let ret = await userList({
		filter,
		pageindex: 1,
		pagesize: 1
	});
	if (!ret) return ret;
	if (ret.msg != "ok") {
		return ret;
	}
	if (ret.data.length > 0) {
		ret.data = ret.data[0];
	} else {
		ret = { msg: "user not found" };
	}
	return ret;
}
// only for admin
export async function userList(params) {
	let pagination = makePager(params);
	const document = gql`
		{
			userList(filter: ${params.filter},${pagination}) {
				totalRows
				userList {
					pageInfo {
						hasPreviousPage
						hasNextPage
						startCursor
						endCursor
					}				
					nodes {
						totalInvited
						accountType {
							id
							name
							isAdmin
						}
						account {
							id
							identity
							email
              address
							accountTypeId
							myInviteCode
							creationTime
							discount
							invitedBy
						}
						storage {
							id
							accountId
							size
							price
						}
					}
				}
			}
		}
	`;
	let ret = await got(document, true);
	if (!ret) return ret;
	let data = ret.data.userList.userList.nodes.map(t => {
		let o = t.account;
		o.totalInvited = t.totalInvited;
		o.accountTypeStr = t.accountType.name;
		o.isAdmin = t.accountType?.isAdmin;
		o.storageSize = t.storage?.size;
		o.key = o.id;
		return o;
	});

	return { msg: "ok", data, total: ret.data.userList.totalRows };
}
export async function operation(params) {
	let pagination = makePager(params);
	const document = gql`
		{
			operation(filter: { fileType: "${params.filter}" },${pagination}) {
				totalRows
				list {
					nodes {
						id
            			containerId
						title
						fid
						displayFid
						fileType
						latestEditor
						creationTime
						sort
					}
				}
			}
		}
	`;
	let ret = await got(document, true);
	if (!ret) return ret;
	let data = ret.data.operation.list.nodes.map(t => {
		return t;
	});
	return { msg: "ok", data, total: ret.data.operation.totalRows };
}

//fontend
export async function fileMetadata(params) {
	console.log({ params });
	const document = gql`
		{
			fileMetadata(filter: {}) {
				id
				accountId
				title
				fid
				displayFid
			}
		}
	`;
	let ret = await got(document, true);
	if (!ret) return ret;
	return ret.data.userList;
}
export async function NewFileMetadata(params) {
	console.log({ params });
	if (typeof params != "string") {
		let arr = ["{"];
		for (let k in params) {
			let v = params[k];
			if (typeof v == "number") {
				arr.push(`${k}:${v}`);
			} else {
				arr.push(`${k}:"${v}"`);
			}
		}
		arr.push("}");
		params = arr.join("\n");
	}
	const document = gql`
		mutation {
			newFileMetadata(fileMetadata: ${params})
		}
	`;
	let ret = await got(document, true);
	if (!ret) return ret;
	if (!ret.data.newFileMetadata) {
		ret.msg = "Save fail";
	}
	if (ret.msg != "ok") {
		antdHelper.alert(ret.msg);
		return ret;
	}
	return ret;
}
export async function updateFileMetadata(params) {
	console.log({ params });
	if (typeof params != "string") {
		let arr = ["{"];
		for (let k in params) {
			let v = params[k];
			if (typeof v == "number") {
				arr.push(`${k}:${v}`);
			} else {
				arr.push(`${k}:"${v}"`);
			}
		}
		arr.push("}");
		params = arr.join("\n");
	}
	const document = gql`
		mutation {
			updateFileMetadata(form: ${params})
		}
	`;
	// console.log(document);return;
	let ret = await got(document, true);
	if (!ret) return ret;
	if (!ret.data.updateFileMetadata) {
		ret.msg = "Save fail";
	}
	if (ret.msg != "ok") {
		antdHelper.alert(ret.msg);
		return ret;
	}
	return ret;
}
export async function deleteFileMetadata(id) {
	const document = gql`
		mutation {
			deleteFileMetadata(form: { id: ${id} })
		}
	`;
	let ret = await got(document, true);
	if (!ret) return ret;
	if (!ret.data.deleteFileMetadata) {
		ret.msg = "Delete fail";
	}
	return ret;
}
export async function discover(params) {
	console.log({ params });
	let pagination = makePager(params);
	const document = gql`
  {
  discover(filter: { fileType: "${params.filter}" },${pagination}){
		totalRows
    list{
      nodes{
            id
            containerId
						title
						fid
						displayFid
						fileType
            shareLink
						latestEditor
						creationTime
						sort
            fileExt
      }
  	}
  }
}
`;
	let ret = await got(document, true);
	if (!ret) return ret;
	let data = ret.data.discover.list.nodes.map(t => {
		t.pic = formatPicUrl(t.displayFid);
		return t;
	});
	return { msg: "ok", data, total: ret.data.discover.totalRows };
}
export async function fileDetail(fid) {
	const document = gql`
  {
  discover(filter: { fid: "${fid}" }, pagination:{after:"0", first:1}){
		totalRows
    list{
      nodes{
            id
            containerId
						title
						fid
						displayFid
						fileType
            shareLink
						latestEditor
						creationTime
						sort
      }
  	}
  }
}
`;
	let ret = await got(document, true);
	if (!ret) return ret;
	if (ret.msg != "ok") {
		console.log(ret);
		antdHelper.alert(ret.msg);
		return null;
	}
	console.log({ ret });
	let data = ret.data.discover.list.nodes.map(t => {
		return t;
	});
	return { msg: "ok", data: data[0] };
}
export async function rewards(params) {
	console.log({ params });
	let pagination = makePager(params);
	const document = gql`
    {
      rewards(${pagination}) {
        totalRewards
        discount
        totalRows
        rewards {
          nodes {
            accountId
            email
            invitedAccounts
            spend
            reward
          }
        }
      }
    }
  `;
	let ret = await got(document, true);
	if (!ret) return ret;
	let data = ret.data.rewards.rewards.nodes;
	if (data && data.length > 0) {
		data.forEach(t => {
			t.spend = formatBalanceStr(t.spend);
			t.reward = formatBalanceStr(t.reward);
		});
	}
	return {
		msg: "ok",
		data,
		total: ret.data.rewards.totalRows,
		totalRewards: formatBalanceStr(ret.data.rewards.totalRewards),
		discount: ret.data.rewards.discount
	};
}

// auth
export async function loginRandomString(identity) {
	const document = gql`
		{
			loginRandomString(form: { identity:"${identity}"})
		}
	`;
	let ret = await got(document, true);
	if (!ret) return ret;
	return ret.data.loginRandomString;
}
export async function userLoginByWallet(identity, signedMessage) {
	identity = identity.trim();
	signedMessage = signedMessage.trim();
	const document = gql`
		{
			userLoginByWallet(form: { identity:"${identity}",signedMessage:"${signedMessage}"}){
        id
        token
        identity
        email
        address
        inviteCode
        accountTypeId
        isFirstLogin
      }
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.data?.userLoginByWallet) {
		let info = ret.data.userLoginByWallet;
		storeUserInfo(info);
	}
	return ret;
}
export async function userLoginByEmail(identity, authCode) {
	identity = identity.trim();
	authCode = authCode.trim();
	const document = gql`
		{
			userLoginByEmail(form: { identity:"${identity}",authCode:"${authCode}"}){
        id
        token
        email
        address
        identity
        inviteCode
        accountTypeId
        isFirstLogin
      }
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.data?.userLoginByEmail) {
		let info = ret.data.userLoginByEmail;
		storeUserInfo(info);
	}
	return ret;
}
function storeUserInfo(info) {
	store.set("token", info.token);
	if (info.inviteCode) {
		store.set("inviteCode", info.inviteCode);
	}
	store.set("userId", info.id);
	store.set("identity", info.identity);
	if (info.address) {
		store.set("address", info.address);
		let account = {};
		if (store.get("account")) {
			account = store.get("account");
		}
		account.address = info.address;
		store.set("account", account);
		store.set("addr", info.address);
	}
	if (info.email) {
		store.set("email", info.email);
	}
	store.set("accountType", info.accountTypeId == 2 ? "email" : "wallet");
}
export async function newAuthCode(identity) {
	identity = identity.trim();
	const document = gql`
		mutation{
			newAuthCode(form: { identity:"${identity}"})
		}
	`;
	let ret = await got(document, true);
	return ret;
}
export async function applyInviteCode(identity, inviteCode) {
	const document = gql`
		mutation{
			applyInviteCode(form: { identity:"${identity}",inviteCode:"${inviteCode}"})
		}
	`;
	let ret = await got(document, true);
	return ret;
}
export async function verifyEmailAndSendBindCode(email) {
	const document = gql`
    mutation {
      verifyEmailAndSendBindCode(form: { email: "${email}"})
    }
  `;
	let ret = await got(document, true);
	return ret;
}
export async function verifyEmail(authCode) {
	const document = gql`
    mutation {
      verifyEmail(form: { authCode: "${authCode}"})
    }
  `;
	let ret = await got(document, true);
	return ret;
}
export async function updateAccount(entity) {
	let arr = [];
	for (let k of Object.keys(entity)) {
		let v = entity[k];
		if (typeof v == "number") {
			arr.push(`${k}:${v}`);
		} else {
			arr.push(`${k}:"${v}"`);
		}
	}
	let form = arr.join(",");
	const document = gql`
		mutation{
			updateAccount(form: {${form}})
		}
	`;
	let ret = await got(document, true);
	return ret;
}

//space
export async function buySpace(spaceInGb) {
	const document = gql`
		mutation{
			buySpace(form: { spaceInGb:${spaceInGb}})
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.buySpace) {
		return true;
	}
	return false;
}
export async function expandSpace(spaceInGb) {
	const document = gql`
		mutation{
			expandSpace(form: { spaceInGb:${spaceInGb}})
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.expandSpace) {
		return true;
	}
	return false;
}
export async function extendTime(timeInDays) {
	const document = gql`
		mutation{
			extendTime(form: { timeInDays:${timeInDays}})
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.extendTime) {
		return true;
	}
	return false;
}

//file manage
export async function queryUserRoot() {
	const document = gql`
		{
			queryUserRoot(pagination: { after: "0", first: 1000 }) {
				containerList {
					nodes {
						id
						parentId
						accountId
						name
					}
				}
				totalRows
			}
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.queryUserRoot) {
		let info = ret.data.queryUserRoot.containerList.nodes;
		ret.data = info;
	}
	return ret;
}
export async function queryDirObject(containerId) {
	const document = gql`
    {
      queryDirObject(
        queryContainerForm: { containerId: ${containerId} }
        pagination: { after: "0", first: 100 }
      ) {
        objectList {
          nodes {
            id
            objectType
            name
          }
        }
        totalRows
      }
    }
  `;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.queryDirObject) {
		let info = ret.data.queryDirObject.objectList.nodes;
		ret.data = info;
	}
	return ret;
}
export async function queryFileDetails(idOrFid) {
	let param;
	if (isNaN(idOrFid)) {
		if (idOrFid.length < 40) {
			param = `shareLink:"${idOrFid}"`;
		} else {
			param = `fid:"${idOrFid}"`;
		}
	} else {
		param = `id:${idOrFid}`;
	}
	const document = gql`
		{
			queryFileDetails(form: {${param}}){
        fileMetadata{
          id
          containerId
          title
          fid
          stat
          displayFid
          fileType
          fileSize
          latestEditor
          shareLink
          creationTime
          sort
          fileExt
        }
        account
      }
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.queryFileDetails) {
		let info = ret.data.queryFileDetails.fileMetadata;
		info.fileSizeStr = formatterSizeStr(info.fileSize);
		info.account = ret.data.queryFileDetails.account;
		ret.data = info;
	}
	return ret;
}
export async function getMyFiles(containerId, keyword) {
	let filter = "{}";
	if (keyword) {
		filter = `{title:"${keyword}"}`;
	}
	const document = gql`
    {
      getMyFiles(form: {containerId:${containerId}},filter:${filter}) {
        containers {
          id
          parentId
          accountId
          name
        }
        childrens {
          id
          parentId
          accountId
          name
        }
        files {
          id
          containerId
          title
          fid
          stat
          displayFid
          fileType
          fileSize
          latestEditor
          shareLink
          creationTime
          sort      
          fileExt
        }
      }
    }
  `;
	let ret = await got(document, true);
	if (!ret || ret.msg != "ok") return ret;
	if (ret.data?.getMyFiles) {
		let info = ret.data.getMyFiles;
		info?.files?.forEach(t => {
			t.name = t.title;
			t.fileSizeStr = formatterSizeStr(t.fileSize);
		});
		ret.data = info;
	}
	return ret;
}
export async function newBucketOrDirectory(pid, name) {
	const document = gql`
		mutation{
			newBucketOrDirectory(form: { id:${pid},name:"${name}"})
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.newBucketOrDirectory) {
		return true;
	}
	return false;
}
export async function moveFileToNewContainer(id, parentId) {
	const document = gql`
		mutation{
			moveFileToNewContainer(moveFileForm: { fileId:${id},parentId:${parentId}})
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.moveFileToNewContainer) {
		return true;
	}
	return false;
}
export async function deleteDirectoriesOrBuckets(id) {
	const document = gql`
		mutation{
			deleteDirectoriesOrBuckets(form: { id:${id}})
		}
	`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.deleteDirectoriesOrBuckets) {
		return true;
	}
	return false;
}
export async function deleteFile(id) {
	const document = gql`
  mutation{
    deleteFile(form: { id:${id}})
  }
`;
	let ret = await got(document, true);
	if (ret && ret.msg == "ok" && ret.data?.deleteFile) {
		return true;
	}
	return false;
}
export async function getchildrenFolders(id) {
	let ret = await getMyFiles(id);
	if (!ret || ret.msg != "ok" || !ret.data.childrens) {
		return [];
	}
	return ret.data.childrens;
}

export async function getFolderTree(id) {
	const children = await getchildrenFolders(id);
	const directory = [];
	for (const child of children) {
		const subdirectory = await getFolderTree(child.id);
		directory.push({
			key: child.id,
			title: child.name,
			children: subdirectory
		});
	}
	return directory;
}
export async function postFile(containerId, file, progressCb) {
	return new Promise((resolve, reject) => {
		try {
			const formdata = new FormData();
			formdata.append("0", file, file.name);
			formdata.append("operations", `{"query": "mutation ($file: Upload!){uploadFile(form:{containerId: ${containerId} newFile: $file},)}", "variables": { "file": null }}`);
			formdata.append("map", '{ "0": ["variables.file"] }');
			const xhr = new XMLHttpRequest();
			xhr.timeout = 1000*3600*12;
			xhr.ontimeout = function(event) {
				antdHelper.alertError('Connect timeout.')
			}
			xhr.open("POST", webconfig.api, true);
			xhr.setRequestHeader("Authorization", store.get("token"));
			xhr.onload = function () {
				if (xhr.status === 200) {
					let data = xhr.response;
					if (data.indexOf("{") == 0) {
						data = JSON.parse(data);
					}
					if (data.data) {
						resolve({ msg: "ok" });
					} else {
						resolve({ msg: data.errors[0].message });
					}
				} else {
					resolve({ msg: xhr.status + " error" });
				}
			};
			xhr.onerror = function (e) {
				resolve({ msg: progressError(e) });
			};
			if (progressCb) {
				let stime = new Date().getTime();
				let sloaded = 0;
				xhr.upload.onprogress = function (e) {
					if (e.lengthComputable) {
						let percentComplete = Math.ceil((e.loaded / e.total) * 100);
						let endTime = new Date().getTime();
						let dTime = (endTime - stime) / 1000;
						let dloaded = e.loaded - sloaded;
						let speed = dloaded / dTime;
						speed = speed / 1024;
						stime = new Date().getTime();
						sloaded = e.loaded;
						let speedUnit = "KB/s";
						if (speed > 1024) {
							speed = speed / 1024;
							speedUnit = "MB/s";
						}
						speed = speed.toFixed(1);
						progressCb({
							percentComplete,
							speed,
							speedUnit,
							xhr
						});
					}
				};
			}
			xhr.send(formdata);
		} catch (e) {
			resolve({ msg: progressError(e) });
		}
	});
}
export async function postFileForWalletAccount(containerId, file, progressCb, msg, signedMsg) {
	return new Promise((resolve, reject) => {
		try {
			const formdata = new FormData();
			formdata.append("0", file, file.name);
			formdata.append(
				"operations",
				`{"query": "mutation ($file: Upload!,$msg: String!,$signedMsg: String!){uploadFile(form:{containerId: ${containerId} newFile: $file,msg: $msg signedMsg: $signedMsg},)}", "variables": { "file": null,"msg":"${msg}","signedMsg":"${signedMsg}" }}`
			);
			formdata.append("map", '{ "0": ["variables.file"], "1":["variables.msg"], "2":["variables.signedMsg"]}');
			const xhr = new XMLHttpRequest();
			xhr.timeout = 1000*3600*12;
			xhr.ontimeout = function(event) {
				antdHelper.alertError('Connect timeout.')
			}
			xhr.open("POST", webconfig.api, true);
			xhr.setRequestHeader("Authorization", store.get("token"));
			xhr.onload = function () {
				if (xhr.status === 200) {
					let data = xhr.response;
					if (data.indexOf("{") == 0) {
						data = JSON.parse(data);
					}
					console.log(data);
					if (data.data) {
						resolve({ msg: "ok" });
					} else {
						resolve({ msg: data.errors[0].message });
					}
				} else {
					resolve({ msg: xhr.status + " error" });
				}
			};
			xhr.onerror = function (e) {
				resolve({ msg: progressError(e) });
			};
			if (progressCb) {
				let stime = new Date().getTime();
				let sloaded = 0;
				xhr.upload.onprogress = function (e) {
					if (e.lengthComputable) {
						let percentComplete = Math.ceil((e.loaded / e.total) * 100);
						let endTime = new Date().getTime();
						let dTime = (endTime - stime) / 1000;
						let dloaded = e.loaded - sloaded;
						let speed = dloaded / dTime;
						speed = speed / 1024;
						stime = new Date().getTime();
						sloaded = e.loaded;
						let speedUnit = "KB/s";
						if (speed > 1024) {
							speed = speed / 1024;
							speedUnit = "MB/s";
						}
						speed = speed.toFixed(1);
						progressCb({
							percentComplete,
							speed,
							speedUnit,
							xhr
						});
					}
				};
			}
			xhr.send(formdata);
		} catch (e) {
			resolve({ msg: progressError(e) });
		}
	});
}

//add file info to db for wallet account
export async function createFile(fid, title, containerId) {
	const document = gql`
		mutation{
			walletUploadFile(form: { fid:"${fid}",title:"${title}",containerId:${containerId}})
		}
	`;
	let ret = await got(document, true);
	if (!ret || ret.msg != "ok") {
		return ret;
	}
	ret.msg = ret.data.walletUploadFile ? "ok" : "fail";
	return ret;
}
export async function saveToMyFiles(shareLink) {
	const document = gql`
  mutation {
    saveToMyFiles(shareLink: "${shareLink}")
  }`;
	let ret = await got(document, true);
	if (!ret || ret.msg != "ok") {
		return ret;
	}
	ret.msg = ret.data.saveToMyFiles ? "ok" : "fail";
	return ret;
}
